﻿using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;




namespace device_query_tool
{
    public partial class DeviceExplorer : Form
    {
        static IPEndPoint localIpep = null;                
        private static UdpClient udpConnect = null;         
        static byte[] udpRecv = new byte[100];              

        public const int MaxDevicesNumber = 3000;           
        public int ClockCountTime = 3000;                   

        Device[] device = new Device[MaxDevicesNumber];     
        static int numOfDevices = 0;
        public String savePath = null;

        public int[] maskBox = { 0, 1, 2, 4, 8, 16, 32, 64, 128, 256 };

        public static int delayTime = 100;

        public DeviceExplorer()
        {
            InitializeComponent();
        }

        private void ButtonAdd_Click(object sender, EventArgs e)
        {
            IPAddress ip = ipAddressTextBox1.Value;
            if (ip.ToString() == "127.0.0.1")
            {
                MessageBox.Show("Loop-back not allowed.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            // 添加IP地址
            if (!ListBoxPointToPointIPAddress.Items.Contains(ip.ToString()))
                this.ListBoxPointToPointIPAddress.Items.Add(ip.ToString());
        }

        private void ButtonRemove_Click(object sender, EventArgs e)
        {
            ListBoxPointToPointIPAddress.Items.Remove(ListBoxPointToPointIPAddress.SelectedItem);
        }

        private void devicePropItem_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)  
            {
                if (e.Node.Level == 1)                               
                {
                    // 查询节点属性并展示
                    getProperties(e.Node);
                }

            }
        }

        private void ComboBoxBrowseMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            int item = ComboBoxBrowseMode.SelectedIndex;
            switch (item)
            {
                // 本地
                case 0:
                    {
                        ipAddressTextBox1.Enabled = false;
                        addP2PAddressBtn.Enabled = false;
                        removeP2PAddressBtn.Enabled = false;
                        RIA.Enabled = false;
                        RSM.Enabled = false;
                        maxRemoteDelay.Enabled = false;
                        break;
                    }

                // p2p
                case 1:
                    {
                        ipAddressTextBox1.Enabled = true;
                        addP2PAddressBtn.Enabled = true;
                        removeP2PAddressBtn.Enabled = true;
                        RIA.Enabled = false;
                        RSM.Enabled = false;
                        maxRemoteDelay.Enabled = false;
                        break;
                    }

                // 远程子网
                case 2:
                    {
                        ipAddressTextBox1.Enabled = false;
                        addP2PAddressBtn.Enabled = false;
                        removeP2PAddressBtn.Enabled = false;
                        RIA.Enabled = true;
                        RSM.Enabled = true;
                        maxRemoteDelay.Enabled = true;
                        break;
                    }
                default: break;
            }
        }

        private void DeviceExplorer_Load(object sender, EventArgs e)
        {
            // 为combobox赋默认值
            ComboBoxBrowseMode.SelectedIndex = 0;

            // 排序
            deviceResultView.TreeViewNodeSorter = new DeviceSorter();

        }

        #region 结果展示逻辑
        // 查询节点属性并显示
        private void getProperties(TreeNode treeNode)
        {
            // 通过节点名获取ip地址
            String nodeIp = treeNode.Text.Split('-')[0];
            // 遍历设备列表，查找属性
            Device findDevice = getPropertiesAll(nodeIp);
            showProperties(findDevice);
        }

        // 查询节点属性
        private Device getPropertiesAll(String ip)
        {
            for (int i = 0; i < numOfDevices; i++)
            {
                if (String.Equals(device[i].ips, ip))
                {
                    return device[i];
                }
            }
            return null;
        }

        // 展示节点信息
        private void showProperties(Device d)
        {
            MessageBox.Show(
                        "Address:               " + d.ips + "\n" +
                        "Vendor:                " + d.Vendor + "\n" +
                        "Product Type:      " + d.ProductType + "\n" +
                        "Product Code       " + d.ProductCode + "\n" +
                        "Device Name:       " + d.DeviceName + "\n" +
                        "Serial Number:     " + d.SerialNumber + "\n"

                        , "Device Properties", MessageBoxButtons.OK, MessageBoxIcon.Information
                        );
        }

        private void addItemTree(String parent, Device device)
        {
            // 查看父节点是否存在 不存在则插入父节点
            bool find = false;
            foreach (TreeNode tn in deviceResultView.Nodes)
            {
                if (tn.Text == parent)
                {
                    find = true;
                }
            }
            if (!find)
            {
                deviceResultView.Nodes.Add(parent, parent, 0);
            }


            deviceResultView.SelectedNode = deviceResultView.Nodes[0];
            // 子节点插入
            deviceResultView.SelectedNode.Nodes.Add(device.ips + "-" + device.DeviceName, device.ips + "-" + device.DeviceName, 1, 1);
            // 父节点展开
            deviceResultView.ExpandAll();
        }

        #endregion

        #region 扫描操作

        private void showSocketError(System.Net.Sockets.SocketException ex)
        {
            MessageBox.Show("Invalid ip address!\n" + ex, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            this.LabelStatusText.Text = "Recieve failed!" + numOfDevices.ToString();
            udpConnect.Close();
            stopScan();
        }

        private void stopScan()
        {
            RefreshButton.Enabled = false;
            ButtonStop.Enabled = false;
            ButtonStart.Enabled = true;
            ComboBoxBrowseMode.Enabled = true;
            scanTime.Enabled = true;
            RefreshButton.Enabled = true;
            BIA.Enabled = true;
            maxRemoteDelay.Enabled = true;

            // 关闭定时器线程
            this.LabelStatusText.Text = "Manually stopped.";
            timer1.Enabled = false;
            timer1.Stop();
        }

        private void ButtonStart_Click(object sender, EventArgs e)
        {
            // 按钮限制
            ButtonStop.Enabled = true;
            ButtonStart.Enabled = false;
            ComboBoxBrowseMode.Enabled = false;
            scanTime.Enabled = false;
            RefreshButton.Enabled = false;
            BIA.Enabled = false;
            maxRemoteDelay.Enabled = false;


            numOfDevices = 0;
            this.deviceResultView.Nodes.Clear();
            this.LabelStatusText.Text = "Sending...";

            // 模式选择 0 1 2 3
            int item = ComboBoxBrowseMode.SelectedIndex;
            switch (item)
            {

                case 0: // 本地广播
                    {
                        try
                        {
                            LocalScan();
                        }
                        catch (System.Net.Sockets.SocketException ex)
                        {
                            showSocketError(ex);
                            return;
                        }
                        break;
                    }
                case 1:
                    {
                        // 统计共有多少条需要发送的 ip 并转化为数组
                        int sumIp = ListBoxPointToPointIPAddress.Items.Count;
                        String[] ipString = new string[sumIp];

                        // 将 ip 转化为 string 存入字符串数组
                        for (int i = 0; i < sumIp; i++)
                        {
                            ipString[i] = ListBoxPointToPointIPAddress.Items[i].ToString();
                        }

                        // 向每个目标发送udp
                        for (int i = 0; i < sumIp; i++)
                        {
                            try
                            {
                                P2PScan(ipString[i]);
                            }
                            catch (System.Net.Sockets.SocketException ex)
                            {
                                showSocketError(ex);
                                LabelDevicesNumber.Text = "Error";
                                deviceResultView.Nodes.Clear();
                                return;
                            }
                        }
                        break;
                    }
                case 2:
                    {
                        // 子网掩码、IP 及广播地址的计算
                        String mask = RSM.Value.ToString();
                        String remoteIp = RIA.Value.ToString();
                        int countIp = 0;
                        String[] boardcastIP = Utils.getBoardIP(remoteIp, mask, ref countIp);

                        this.deviceResultView.Nodes.Clear();

                        this.LabelStatusText.Text = "Sending...";

                        try
                        {
                            RemoteScan(boardcastIP);
                        }
                        catch (System.Net.Sockets.SocketException ex)
                        {
                            showSocketError(ex);
                            return;
                        }
                        break;
                    }
                default: break;
            }

            // 显示共捕捉输出设备数
            this.LabelDevicesNumber.Text = numOfDevices.ToString();
            this.LabelStatusText.Text = "Successfully find " + numOfDevices.ToString() + " devices.";

            openTimer();
            if (numOfDevices > 0) devicePropItem.Enabled = true;
        }

        private void ButtonStop_Click(object sender, EventArgs e)
        {
            stopScan();
        }

        private void ButtonClear_Click(object sender, EventArgs e)
        {
            this.deviceResultView.Nodes.Clear();
            devicePropItem.Enabled = false;
            numOfDevices = 0;
        }

        #endregion

        #region 定时器相关
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (checkBox1.Checked == false)
            {
                RefreshButton.Enabled = true;
                timer1.Stop();
            }
            else
            {
                // 按钮限制
                ButtonStop.Enabled = true;
                ButtonStart.Enabled = false;
                ComboBoxBrowseMode.Enabled = false;
                scanTime.Enabled = false;
                RefreshButton.Enabled = false;
                BIA.Enabled = false;
                maxRemoteDelay.Enabled = false;

                // 更改状态
                numOfDevices = 0;
                this.deviceResultView.Nodes.Clear();
                this.LabelStatusText.Text = "Sending...";


                // 模式选择 0 1 2 3
                int item = ComboBoxBrowseMode.SelectedIndex;
                switch (item)
                {
                    // 本地广播
                    case 0:
                        {
                            try
                            {
                                LocalScan();
                            }
                            catch (System.Net.Sockets.SocketException ex)
                            {
                                showSocketError(ex);
                                return;
                            }
                            break;
                        }
                    case 1:
                        {
                            // 统计需要ip的数目并转化为数组
                            int sumIp = ListBoxPointToPointIPAddress.Items.Count;
                            String[] ipString = new string[sumIp];

                            // 将ip转化为 string 存入字符串数组
                            for (int i = 0; i < sumIp; i++)
                            {
                                ipString[i] = ListBoxPointToPointIPAddress.Items[i].ToString();
                            }

                            // 向每个目标发送 udp
                            for (int i = 0; i < sumIp; i++)
                            {
                                try
                                {
                                    P2PScan(ipString[i]);
                                }
                                catch (System.Net.Sockets.SocketException ex)
                                {      
                                    showSocketError(ex);
                                    LabelDevicesNumber.Text = "Error";
                                    deviceResultView.Nodes.Clear();
                                    return;
                                }
                            }
                            break;
                        }
                    case 2:
                        {
                            // 子网掩码、IP及广播地址的计算
                            String mask = RSM.Value.ToString();
                            String remoteIp = RIA.Value.ToString();
                            int countIp = 0;
                            String[] boardcastIP = Utils.getBoardIP(remoteIp, mask, ref countIp);

                            try
                            {
                                RemoteScan(boardcastIP);
                            }
                            catch (System.Net.Sockets.SocketException ex)
                            {                            
                                showSocketError(ex);
                                return;
                            }
                            break;
                        }
                    default: break;
                }
                // 显示共捕捉输出设备数
                this.LabelDevicesNumber.Text = numOfDevices.ToString();
                this.LabelStatusText.Text = "Successfully find " + numOfDevices.ToString() + " devices.";

                openTimer();
            }
        }
        
        private void openTimer()
        {
            //定时器开始
            timer1.Interval = ClockCountTime;
            timer1.Enabled = true;
            timer1.Start();
        }

        private void closeTimer()
        {
            timer1.Enabled = false;
            timer1.Stop();
        }

        #endregion

        #region 后端 udp 连接实现

        // UDP 解包
        private void packageUnwarp(ref Device device, byte[] vs, IPAddress ip)
        {
            // 为 device 分配存储空间，并根据不同的缓冲区位解码
            device = new Device();
            device.ips = ip.ToString();
            device.Vendor = udpRecv[48] + udpRecv[49] * 16;
            device.ProductType = udpRecv[50] + udpRecv[51] * 16;
            device.ProductCode = udpRecv[52] + udpRecv[53] * 16;
            device.SerialNumber = "0x" +
                Convert.ToString(udpRecv[61], 16) +
                Convert.ToString(udpRecv[60], 16) +
                Convert.ToString(udpRecv[59], 16) +
                Convert.ToString(udpRecv[58], 16);
            device.DeviceName = Utils.byteToString(vs, 63, vs.Length - 1);
        }

        private int LocalScan()
        {
            // 本机IP和监听端口号
            localIpep = new IPEndPoint(BIA.Value, 44814);

            // 发送UDP访问报文
            udpConnect = new UdpClient(localIpep);
            udpConnect.Client.ReceiveTimeout = 100;
            udpConnect.Client.ReceiveBufferSize = 204800;
            byte[] sendbytes = { 0x63, 00, 00, 00, 00, 00, 00,
                            00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
                            00, 00, 00, 00, 00 };

            IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Parse("255.255.255.255"), 44818); // 发送到的IP地址和端口号
            udpConnect.Send(sendbytes, sendbytes.Length, remoteIpep);

            // 接收回传报文
            try
            {
                while (true)
                {
                    // udpRecv接收数组，单次接收
                    // 读取缓冲区数据
                    udpRecv = udpConnect.Receive(ref localIpep);
                    IPAddress ip = localIpep.Address;

                    // 数组解包
                    packageUnwarp(ref device[numOfDevices], udpRecv, ip);


                    // 当index大于最大设备数组，重置index
                    if (numOfDevices >= MaxDevicesNumber)
                    {
                        MessageBox.Show("超出设备上限！", "警告",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                        numOfDevices = 0;
                    }

                    // 获取主机名
                    string name = Utils.getHostNameByIP(BIA.Value.ToString());

                    // 将设备添加到treeview中
                    addItemTree(name, device[numOfDevices]);
                    numOfDevices++;

                    // 作用：防止界面假死
                    // 代价：增加加载时间
                    //Application.DoEvents();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                // 关闭udp连接
                udpConnect.Close();
            }
            return 0;
        }

        private int P2PScan(String ipString)
        {
            // 配置发送Udp
            localIpep = new IPEndPoint(BIA.Value, 44814); // 本机IP和监听端口号
            udpConnect = new UdpClient(localIpep);
            udpConnect.Client.ReceiveTimeout = 100;
            udpConnect.Client.ReceiveBufferSize = 204800;
            byte[] sendbytes = { 0x63, 00, 00, 00, 00, 00, 00,
                            00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
                            00, 00, 00, 00, 00 };
            //发送
            IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Parse(ipString), 44818); // 发送到的IP地址和端口号
            udpConnect.Send(sendbytes, sendbytes.Length, remoteIpep);

            try
            {
                // 接收udp，捕捉接收超时Exception
                udpRecv = udpConnect.Receive(ref localIpep);
            }
            catch (Exception)
            {

            }
            finally
            {
                IPAddress ip = localIpep.Address;
                if (!String.Equals(ip.ToString(), "127.0.0.1"))
                {
                    // 对接受进行解码
                    packageUnwarp(ref device[numOfDevices], udpRecv, ip);
                    string name = Utils.getHostNameByIP(BIA.Value.ToString());
                    addItemTree(name, device[numOfDevices]);

                    numOfDevices++;
                    if (numOfDevices >= MaxDevicesNumber)
                    {
                        MessageBox.Show("超出设备上限！", "警告",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                        numOfDevices = 0;
                    }
                }
                udpConnect.Close();
            }
            return 0;
        }

        private int RemoteScan(String[] boardcastIP)
        {
            ButtonStop.Enabled = false;
            for (int i = 0; i < boardcastIP.Length; i++)
            {
                if (String.Equals(boardcastIP[i], "127.0.0.0"))
                {
                    continue;
                }
                // 本机IP和监听端口号
                localIpep = new IPEndPoint(BIA.Value, 44814);

                // 发送UDP访问报文
                udpConnect = new UdpClient(localIpep);
                udpConnect.Client.ReceiveTimeout = delayTime;
                udpConnect.Client.ReceiveBufferSize = 204800;
                byte[] sendbytes = { 0x63, 00, 00, 00, 00, 00, 00,
                        00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
                        00, 00, 00, 00, 00 };

                IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Parse(boardcastIP[i]), 44818); // 发送到的IP地址和端口号
                udpConnect.Send(sendbytes, sendbytes.Length, remoteIpep);

                // 接收回传报文
                try
                {
                    // udpRecv接收数组，单次接收
                    // 读取缓冲区数据
                    udpRecv = udpConnect.Receive(ref localIpep);
                    IPAddress ip = localIpep.Address;
                    if (String.Equals(ip.ToString(), "127.0.0.1"))
                    {
                        continue;
                    }

                    // 数组解包
                    packageUnwarp(ref device[numOfDevices], udpRecv, ip);

                    // 当index大于最大设备数组，重置index
                    if (numOfDevices >= MaxDevicesNumber)
                    {
                        MessageBox.Show("超出设备上限！", "警告",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
                        numOfDevices = 0;
                    }
                    // 将设备添加到treeview中
                    string name = RIA.Value.ToString() + "所在的子网";
                    addItemTree(name, device[numOfDevices]);
                    numOfDevices++;
                    Application.DoEvents();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
                finally
                {
                    // 关闭udp连接
                    udpConnect.Close();
                }
            }
            ButtonStop.Enabled = true;
            return 0;
        }
        #endregion

        private void ButtonSave_Click(object sender, EventArgs e)
        {

            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "Please select the file path:";

            DialogResult dialogResult = dialog.ShowDialog();
            if (dialogResult == DialogResult.OK)
            {
                string foldPath = dialog.SelectedPath;
                DirectoryInfo theFolder = new DirectoryInfo(foldPath);
                savePath = theFolder.FullName;
            }
            else if (dialogResult == DialogResult.Cancel)
            {
                return;
            }

            try
            {
                if (numOfDevices == 0)
                {
                    MessageBox.Show("No devices to save!", "Warn", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }
                String[] nowTimes = DateTime.Now.ToLongTimeString().ToString().
                    Split(new char[] { '/', ':' });
                String nowTime = nowTimes[0] + "-" + nowTimes[1] + "-" + nowTimes[2];
                String[] DataNames = DateTime.Now.ToShortDateString().ToString().Split(new char[] { '/', ':', '-' });
                String DataName = DataNames[0] + "-" + DataNames[1] + "-" + DataNames[2];
                String fileName = DataName + "-" + nowTime + "-" + "Device.txt";
                StreamWriter streamWriter = new StreamWriter(savePath + "\\" + fileName, true);

                // 遍历所有根节点
                foreach (TreeNode n in deviceResultView.Nodes)
                {
                    streamWriter.WriteLine(n.Text.ToString());
                    foreach (TreeNode child in n.Nodes)
                    {
                        Device findDevice = getPropertiesAll(child.Text.Split('-')[0]);
                        streamWriter.WriteLine("    Device Name:" + findDevice.DeviceName);
                        streamWriter.WriteLine("    Device Address:" + findDevice.ips);
                        streamWriter.WriteLine("    Product Type:" + findDevice.ProductType);
                        streamWriter.WriteLine("    Product Code:" + findDevice.ProductCode);
                        streamWriter.WriteLine("    Serial Number:" + findDevice.SerialNumber);
                        streamWriter.WriteLine(" ");
                    }
                }
                MessageBox.Show("Save success!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
                streamWriter.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to save:\n" + ex, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }

        private void devicePropItem_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                if (numOfDevices == 0) return;
                TreeNode tn = deviceResultView.GetNodeAt(e.X, e.Y);
                if (e.Node.Level == 1)
                {
                    deviceResultView.SelectedNode = tn;
                }
            }
        }

        private void devicePropItem_Click(object sender, EventArgs e)
        {
            getProperties(deviceResultView.SelectedNode);
        }

        private void scanTime_ValueChanged(object sender, EventArgs e)
        {

            ClockCountTime = (int)this.scanTime.Value;
        }

        private void maxRemoteDelay_ValueChanged(object sender, EventArgs e)
        {
            delayTime = (int)this.maxRemoteDelay.Value;
        }
    }

}